package cn.wizzer.app.crown.modules.services.impl;

import cn.wizzer.app.crown.modules.models.CrownBetCount;
import cn.wizzer.app.crown.modules.models.enums.PlayWayEnum;
import cn.wizzer.app.crown.modules.models.vo.CrownBetCountVo;
import cn.wizzer.app.crown.modules.models.vo.CrownOrderIncomeVo;
import cn.wizzer.app.crown.modules.services.order.CrownOrderService;
import cn.wizzer.app.sys.modules.services.SysParamService;
import cn.wizzer.app.user.modules.models.User;
import cn.wizzer.app.user.modules.models.UserAccountRecord;
import cn.wizzer.app.user.modules.models.enums.BizTypeEnum;
import cn.wizzer.app.user.modules.services.CrownBetCountService;
import cn.wizzer.app.user.modules.services.UserRestrictionService;
import cn.wizzer.app.user.modules.services.UserService;
import cn.wizzer.app.user.modules.services.account.UserAccountRecordService;
import cn.wizzer.framework.base.Result;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONObject;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = CrownBetCountService.class)
public class CrownBetCountServiceImpl extends BaseServiceImpl<CrownBetCount> implements CrownBetCountService {
    public CrownBetCountServiceImpl(Dao dao) {
        super(dao);
    }

    private static final Log log = Logs.get();

    @Inject
    @Reference
    private SysParamService sysParamService;
    @Inject
    @Reference
    private UserService userService;

    @Inject
    @Reference
    private UserRestrictionService restrictionService;
    @Inject
    @Reference
    private UserAccountRecordService accountRecordService;
    @Inject
    private CrownOrderService orderService;
    @Inject
    private CommonDBService dbService;

    @Override
    public Map recordCount(String appid) {
        //today 00:00
        long today = Times.ams(Times.nextDay(0)) / 1000;
        //yesterday 00:00
        long yesterday = Times.ams(Times.befoDay()) / 1000;
        Calendar instance = Calendar.getInstance();
        instance.set(Calendar.DATE, 1);
        instance.set(Calendar.HOUR, 0);
        instance.set(Calendar.MINUTE, 0);
        instance.set(Calendar.SECOND, 0);
        //this month 1th 00:00
        long thisMonth = instance.getTime().getTime() / 1000;
        instance.add(Calendar.MONTH, -1);
        //last month 1th 00:00
        long lastMonth = instance.getTime().getTime() / 1000;
        //共计
        BigDecimal all = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select r.* from user_account_record r " +
                "left join crown_bet_count c on r.biz_type='SUB_REBATE' and c.id = r.biz_id " +
                "where c.inviteId='" + appid + "'").stream().map(UserAccountRecord::getOperateAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);
        //昨天
        BigDecimal yesterdaySum = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select r.* from user_account_record r " +
                "left join crown_bet_count c on r.biz_type='SUB_REBATE' and c.id = r.biz_id " +
                "where c.inviteId='" + appid + "' " +
                "and r.opAt between " + yesterday + " and " + today).stream().map(UserAccountRecord::getOperateAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);
        //本月
        BigDecimal thisMonthSum = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select r.* from user_account_record r " +
                "left join crown_bet_count c on r.biz_type='SUB_REBATE' and c.id = r.biz_id " +
                "where c.inviteId='" + appid + "' " +
                "and r.opAt >= " + thisMonth).stream().map(UserAccountRecord::getOperateAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);
        //上个月
        BigDecimal lastMonthSum = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select r.* from user_account_record r " +
                "left join crown_bet_count c on r.biz_type='SUB_REBATE' and c.id = r.biz_id " +
                "where c.inviteId='" + appid + "' " +
                "and r.opAt between " + lastMonth + " and " + thisMonth).stream().map(UserAccountRecord::getOperateAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);

//        //共计
//        BigDecimal all = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select * from user_account_record " +
//                "where biz_type='SUB_REBATE' and user_id = '" + appid + "' ").stream().map(UserAccountRecord::getOperateAmount)
//                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);
//        //昨天
//        BigDecimal yesterdaySum = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select * from user_account_record " +
//                "where biz_type='SUB_REBATE' and user_id = '" + appid + "' "+
//                "and opAt between " + yesterday + " and " + today).stream().map(UserAccountRecord::getOperateAmount)
//                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);
//        //本月
//        BigDecimal thisMonthSum = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select * from user_account_record " +
//                "where biz_type='SUB_REBATE' and user_id = '" + appid + "' "+
//                "and opAt >= " + thisMonth).stream().map(UserAccountRecord::getOperateAmount)
//                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);
//        //上个月
//        BigDecimal lastMonthSum = dbService.<UserAccountRecord>list(UserAccountRecord.class, "select r.* from user_account_record " +
//                "where biz_type='SUB_REBATE' and user_id = '" + appid + "' "+
//                "and opAt between " + lastMonth + " and " + thisMonth).stream().map(UserAccountRecord::getOperateAmount)
//                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.DOWN);

        return Result.createMap("all", all,
                "yesterday", yesterdaySum,
                "thisMonth", thisMonthSum,
                "lastMonth", lastMonthSum);
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public synchronized void countResult() {
        //region 已结算未统计注单数据
        //查询零点之前所有已结算且未统计订单
        long todayZero = Times.ams(Times.nextDay(0)) / 1000;
        Sql sql = Sqls.create("SELECT o.id as orderId, u.id as userId, u.inviter as inviteId, o.play_way as playWay, o.settleAt, o.amount, v.odds")
                .appendSourceSql("FROM crown_order o ")
                .appendSourceSql("LEFT JOIN (SELECT cod.order_id, CASE co.play_way WHEN 'P3' THEN '0' ELSE cod.odds END as odds " +
                        "FROM crown_order_detail cod " +
                        "LEFT JOIN crown_order co ON cod.order_id = co.id " +
                        "GROUP BY cod.order_id) v ON v.order_id = o.id ")
                .appendSourceSql("LEFT JOIN USER u ON o.user_id = u.id ")
                .appendSourceSql("WHERE o.status = 'SETTLED' " +
                        "AND counted = 0 ")
                .appendSourceSql("AND o.settleAt < @todayZero ")
                .setParam("todayZero",todayZero);
        List<CrownOrderIncomeVo> crownOrderIncomeVoList = dbService.list(CrownOrderIncomeVo.class, sql.toString());

        //endregion
        //region 统计到相关记录（日期+用户）
        Map<String, CrownBetCount> betCounts = new HashMap<>();
        //查询全局配置的有效赔率
        BigDecimal validOdds = sysParamService.fetch().getValidOdds();
        //当前时间
        Long now = System.currentTimeMillis();
        crownOrderIncomeVoList.forEach(crownOrderIncomeVo -> {
            if (!crownOrderIncomeVo.getPlayWay().equals(PlayWayEnum.P3)) {
                if (crownOrderIncomeVo.getOdds().compareTo(validOdds) <= 0) {
                    return;
                }
            }
            //用户id
            String userId = crownOrderIncomeVo.getUserId();
            //上级id
            String inviteId = crownOrderIncomeVo.getInviteId();
            //日期
            String date = Times.format("yyyy-MM-dd", Times.D(crownOrderIncomeVo.getSettleAt() * 1000L));
            String key = userId + inviteId + date;
            CrownBetCount crownBetCount = betCounts.get(key);
            if (crownBetCount == null) {
                //数据库中查询是否已有此数据
                crownBetCount = this.fetch(Cnd.where("date", "=", date).and("userId", "=", userId).and("inviteId", "=", inviteId));
                if (crownBetCount == null) {
                    //若无则创建
                    crownBetCount = new CrownBetCount();
                    crownBetCount.setDate(date);
                    crownBetCount.setCount(BigDecimal.ZERO);
                    crownBetCount.setCtAt(now);
                    crownBetCount.setUserId(userId);
                    crownBetCount.setInviteId(userService.fetch(userId).getInviter());
                    crownBetCount.setOpAt(1L);
                }
                betCounts.put(key, crownBetCount);
            }
            //累加注额
            crownBetCount.setCount(crownBetCount.getCount().add(crownOrderIncomeVo.getAmount()));
        });

        //endregion
        try {
            //region 保存统计数据
            List<String> orderIds = crownOrderIncomeVoList.stream().map(CrownOrderIncomeVo::getOrderId).collect(Collectors.toList());
            //批量修改订单统计状态
            orderService.update(Chain.make("counted", 1), Cnd.where("id", "IN", orderIds));
            //新增或更新注单统计
            betCounts.forEach((key, betCount) -> this.insertOrUpdate(betCount));
            //endregion
            //region 注单统计已完成项结算到代理账户
            betCounts.values().forEach(betCount -> {
                if (Strings.isNotBlank(betCount.getInviteId())) {
                    //用户为代理
                    if (userService.fetch(Cnd.where("id", "=", betCount.getInviteId())).isAgent()) {
                        //上级返水
                        BigDecimal superRate = restrictionService.initCache(betCount.getUserId()).getBigDecimalSuperRate().divide(new BigDecimal(10000), 6, RoundingMode.DOWN);
                        if (superRate.compareTo(BigDecimal.ZERO) > 0) {
                            //返水
                            BigDecimal income = betCount.getCount().multiply(superRate).setScale(2, RoundingMode.DOWN);
                            if(income.compareTo(BigDecimal.ZERO) > 0){
                                accountRecordService.add(betCount.getInviteId(),
                                        income,
                                        BizTypeEnum.SUB_REBATE, betCount.getId());
                            }
                            betCount.setAgentIncome(income);
                        }
                    }
                }

                //自返
                BigDecimal selfRate = restrictionService.initCache(betCount.getUserId()).getBigDecimalSelfRate().divide(new BigDecimal(10000), 6, RoundingMode.DOWN);
                if (selfRate.compareTo(BigDecimal.ZERO) > 0) {
                    //返水
                    BigDecimal income = betCount.getCount().multiply(selfRate).setScale(2, RoundingMode.DOWN);
                    if(income.compareTo(BigDecimal.ZERO) > 0){
                        accountRecordService.add(betCount.getUserId(),
                                income,
                                BizTypeEnum.SELF_REBATE, betCount.getId());
                    }
                    betCount.setSelfIncome(income);
                }
                //更新数据
                this.update(betCount);
            });
            //endregion
        } catch (Exception e) {
            //for rollback
            e.printStackTrace();
            log.error(e);
            throw new RuntimeException();
        }
    }

    /**
     * 分页查询代理返水记录
     *
     * @param userSearch
     * @param inviterSearch
     * @param searchDate
     * @param pageNumber
     * @param pageSize
     * @return
     */
    @Override
    public Pagination listPage(String userSearch, String inviterSearch, String[] searchDate, int pageNumber, int pageSize) {
        //列表sql
        Sql sql = Sqls.create("SELECT cbc.id, cbc.date, cbc.count, cbc.userId, cbc.inviteId, cbc.selfIncome, cbc.agentIncome, cbc.ctAt, " +
                "u1.phone, u1.nickName, u1.userName, " +
                "u2.phone as inviterPhone, u2.nickName as inviterNickName, u2.userName as inviterUserName ");
        //总数sql
        Sql countSql = Sqls.create("SELECT COUNT(1) ");
        String sqlStr = "FROM crown_bet_count cbc " +
                "LEFT JOIN user u1 ON cbc.userId = u1.id " +
                "LEFT JOIN user u2 ON cbc.inviteId = u2.id " +
                "WHERE cbc.delFlag = 0";
        sql.appendSourceSql(sqlStr);
        countSql.appendSourceSql(sqlStr);
        if(Strings.isNotBlank(userSearch)){
            String cndStr = "AND (u1.phone LIKE CONCAT('%', @userSearch, '%') " +
                    "OR u1.nickName LIKE CONCAT('%', @userSearch, '%') " +
                    "OR u1.userName LIKE CONCAT('%', @userSearch, '%') )";
            sql.appendSourceSql(cndStr);
            sql.setParam("userSearch", userSearch);
            countSql.appendSourceSql(cndStr);
            countSql.setParam("userSearch", userSearch);
        }
        if(Strings.isNotBlank(inviterSearch)){
            String cndStr = "AND (u2.phone LIKE CONCAT('%', @inviterSearch, '%') " +
                    "OR u2.nickName LIKE CONCAT('%', @inviterSearch, '%') " +
                    "OR u2.userName LIKE CONCAT('%', @inviterSearch, '%') )";
            sql.appendSourceSql(cndStr);
            sql.setParam("inviterSearch", inviterSearch);
            countSql.appendSourceSql(cndStr);
            countSql.setParam("inviterSearch", inviterSearch);
        }
        if(searchDate != null && searchDate.length == 2){
            String cndStr = "AND cbc.date BETWEEN @startTime AND @endTime";
            sql.appendSourceSql(cndStr);
            sql.setParam("startTime", searchDate[0]);
            sql.setParam("endTime", searchDate[1]);
            countSql.appendSourceSql(cndStr);
            countSql.setParam("startTime", searchDate[0]);
            countSql.setParam("endTime", searchDate[1]);
        }
        sql.appendSourceSql("ORDER BY cbc.date DESC");
        return dbService.listPage(CrownBetCountVo.class, pageNumber, pageSize, sql.toString(), countSql.toString());
    }

    @Override
    public Map agentIncomeRecord(Integer pageNumber, int pageSize, String startTime, String endTime, String searchKeyword, String userId) {
        //用户集合map
        Map<String, String> userMap = new HashMap<>();
        //列表sql
        Sql sql = Sqls.create("SELECT id, date, userId, agentIncome, ctAt FROM crown_bet_count " +
                "$condition ");
        //总数sql
        Sql countSql = Sqls.create("SELECT COUNT(1) FROM crown_bet_count " +
                "$condition ");
        //求和sql
        Sql sumSql = Sqls.create("SELECT IFNULL(SUM(agentIncome), 0) FROM crown_bet_count " +
                "$condition ");
        //条件
        Cnd cnd = Cnd.where("inviteId", "=", userId);
        if (Strings.isNotBlank(startTime)) {
            cnd.and("date", ">=", startTime);
        }
        if (Strings.isNotBlank(endTime)) {
            cnd.and("date", "<=", endTime);
        }
        if (Strings.isNotBlank(searchKeyword)) {
            //模糊查询用户，若有结果则转为用户集合map，若无结果则返回空数据
            searchKeyword = "%" + searchKeyword + "%";
            List<User> query = userService.query(Cnd.where(Cnd.exps("nickName", "LIKE", searchKeyword)
                    .or("userName", "LIKE", searchKeyword)
                    .or("phone", "LIKE", searchKeyword))
                    .and("delFlag", "=", 0));
            if(query.size() > 0){
                List<String> userIds = query.stream().map(User::getId).collect(Collectors.toList());
                userMap = userService.usersToMapJSONString(query);
                cnd.and("userId", "IN", userIds);
            }else {
                cnd.and("userId", "IS", null);
            }
        }
        sql.setCondition(cnd);
        sql.appendSourceSql("ORDER BY date DESC");
        countSql.setCondition(cnd);

        Pagination pagination = dbService.listPage(CrownBetCountVo.class, pageNumber, pageSize, sql.toString(), countSql.toString());
        List<CrownBetCountVo> crownBetCountVoList = pagination.getList(CrownBetCountVo.class);
        if(crownBetCountVoList.size() > 0){
            if(userMap.size() == 0){
                List<String> userIds = crownBetCountVoList.stream().map(CrownBetCountVo::getUserId).distinct().collect(Collectors.toList());
                userMap = userService.usersToMapJSONString(userService.query(Cnd.where("id", "IN", userIds)));
            }
            for (CrownBetCountVo crownBetCountVo : crownBetCountVoList) {
                User user = JSONObject.parseObject(userMap.get(crownBetCountVo.getUserId()), User.class);
                crownBetCountVo.setPhone(user.getPhone());
                crownBetCountVo.setNickName(user.getNickName());
                crownBetCountVo.setUserName(user.getUserName());
            }
        }

        sumSql.setCondition(cnd);
        sumSql.setCallback(Sqls.callback.str());
        this.dao().execute(sumSql);
        return Result.createMap("pagination", pagination,
                "countAmount", new BigDecimal(sumSql.getString()).setScale(2, RoundingMode.DOWN));
    }

}
